#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
##############################################################################
#
# Description: A pyglet sample on how to animate a character.
# Author: Kao Cardoso Félix.
#
#   Send any questions or suggestions to 'kcfelix [at] gmail [dot] com'
#
##############################################################################
import pyglet

from pyglet.image import Animation, AnimationFrame
from pyglet.sprite import Sprite

class Rm2kCharacter(object):
    """ This class represents a character in RPG Maker 2000 style.
    
    This class loads and display a character from an RPM Maker 2000 style
    charset.
    """
    char_w = 24
    char_h = 32
    
    def __init__(self, img, pos=(0,0), scale=2.0):
        """Initialize a rm2k character

        :Parameters:
                `img` : string or image
                    this should point to a RPG Maker 2000 style character sheet.
                `position` : tuple
                    initial position of the character's sprite
                `scale` : float
                    initial scale of the character's sprite
        """
        
        super(Rm2kCharacter, self).__init__()
        
        # Some defaults
        self.facing = 'S'
        self.is_walking = False
        self.speed = 3
        
        # These very simple line give us the possibility of passing
        # a string to our character instead of loading a image ourselves
        # Borrowed from cocos2d Sprite class
        #
        if isinstance(img, str):
            img = pyglet.resource.image(img)
            
            
        # Pyglet's image origin is located at the bottom-left
        # corner and y increases up, so this code gets the frames
        # from bottom to top.
        # The order is like this:
        #   4 --->
        #   3 --->
        #   2 --->
        #   1 --->
        #
        cw, ch = self.char_w, self.char_h
        char_frames = [img.get_region(x * self.char_w, y * self.char_h, cw, ch) for y in range(4) for x in range(3)]
        
        # Center the image anchors on the center
        # so that the sprite scaling can be relative to the center
        #
        for frame in char_frames:
            frame.anchor_x = frame.width / 2
            frame.anchor_y = frame.height / 2
        
        
        # Setup stills and animations
        #
        self.stills = {}
        self.anims = {}
        
        for i, dir in enumerate(('W', 'S', 'E', 'N')):
            # Still images are located in the middle row of the
            # charset. We start at index 1 and go 3 imgs at a time
            #
            self.stills[dir] = char_frames[1 + i*3]
            
            # Get the animation frames for the current direction.
            # Always groups of 3 frames that were on the same row.
            #
            frame_imgs = char_frames[i*3: (i*3)+3]
            self.anims[dir] = Animation([
                AnimationFrame(frame_imgs[0], .2), # A left step frame
                AnimationFrame(frame_imgs[1], .2), # Another still frame
                AnimationFrame(frame_imgs[2], .2), # A right step frame
                AnimationFrame(frame_imgs[1], .2), # A still frame
                ])
            
        # Create the sprite for our character
        px, py = pos
        self.sprite = Sprite(self.stills[self.facing], x=px, y=py)
        self.sprite.scale = scale
            
    def walk(self, dir):
        """ Makes the walk animation play on a given dir.
        
        It's safe to call multiple times on the same dir without
        restarting the animation.
        """
        
        if self.facing != dir or not self.is_walking:
            self.facing = dir
            self.is_walking = True
            self._update_sprite()
        
    def stop(self):
        """ Stop the walk animation """
        
        self.is_walking = False
        self._update_sprite()
        
    def update(self):
        if self.is_walking:
            if self.facing == 'N':
                self.sprite.y += self.speed
            elif self.facing == 'S':
                self.sprite.y -= self.speed
            elif self.facing == 'E':
                self.sprite.x += self.speed
            elif self.facing == 'W':
                self.sprite.x -= self.speed
                
    def _update_sprite(self):
        imgs = self.anims if self.is_walking else self.stills
        self.sprite.image = imgs[self.facing]
    
if __name__ == '__main__':
    from pyglet.window import key
    
    win = pyglet.window.Window()
     
    faid = Rm2kCharacter('faid.png', (320, 240))
        
    # Create a keyboard handler for input checking
    keyboard = key.KeyStateHandler()
    win.push_handlers(keyboard)
    
    # A draw event for our window
    @win.event
    def on_draw():
        win.clear()
        faid.sprite.draw()
        
    def handle_input(dt):
        if keyboard[key.UP]:
            faid.walk('N')
        elif keyboard[key.RIGHT]:
            faid.walk('E')
        elif keyboard[key.LEFT]:
            faid.walk('W')
        elif keyboard[key.DOWN]:
            faid.walk('S')
        else:
            faid.stop()
            
    def update(dt):
        faid.update()
        
    # Schedule handle input to be called each frame
    pyglet.clock.schedule(handle_input)
            
    # Schedule update to be called at 60 fps
    pyglet.clock.schedule_interval(update, 1/60.0)
            
    # At last, run our app!
    pyglet.app.run()
